"""
    Selenium 实用工具
"""
import os
import platform
import requests 
from typing import List
from urllib import parse
from selenium.common import TimeoutException
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support.expected_conditions import *
from selenium.webdriver import Chrome, ChromeOptions, ActionChains


class ChromeHelper(Chrome):
    """
         谷歌浏览器助手
    """

    def __init__(self, wait=0, is_headless=False):
        options = ChromeOptions()
        options.add_argument("--no-sandbox")
        options.add_argument('--disable-gpu')
        if is_headless: options.add_argument('--headless')  # 无界面浏览器
        options.add_argument("user-code_data-dir=" + self.__get_user_data_dir())  # 设置用户数据目录
        options.add_argument("--disable-blink-features=AutomationControlled")  # 禁用启用Blink运行时的功能
        options.add_experimental_option("excludeSwitches", ["enable-automation"])  # 去除浏览器检测框
        # options.add_argument('blink-settings=imagesEnabled=false')  # 不加载图片, 提升速度
        # options.add_argument('blink-settings=imagesEnabled=false')  # 不加载图片, 提升速度

        # 浏览器参数 --remote-debugging-port=9222
        # options.add_experimental_option('debuggerAddress', '127.0.0.1:9222')
        super().__init__(options=options)
        self.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": """
                                Object.defineProperty(navigator, 'webdriver', {
                                    get: () => undefined
                                })
                """})  # 再次覆盖window.navigator.webdriver的值
        # 隐式等待
        self.implicitly_wait(wait)

    def scroll_by(self, x=0, y=0):
        """
            滑动滚轮
        :param x:水平方向
        :param y:垂直方向
        """
        self.execute_script("window.scrollBy(%s,%s)" % (x, y))

    def wait_presence_of_element(self, by, value, timeout=5, interval=0.5) -> WebElement:
        """
            等待单个元素出现
        :param by:查找依据
        :param value:查找元素
        :param timeout:等待最长时间
        :param interval:检测时间间隔
        :return:查找到的元素,如果没有找到返回None
        """
        try:
            return WebDriverWait(self, timeout, interval).until(presence_of_element_located((by, value)))
        except:
            pass

    def wait_leave_of_element(self, by, value, timeout=5, interval=0.5) -> bool:
        """
            等待单个元素离开
        :param by:查找依据
        :param value:查找元素
        :param timeout:等待最长时间
        :param interval:检测时间间隔
        :return:是否离开
        """
        try:
            # 如果规定时间内离开返回True, 否则报错
            return WebDriverWait(self, timeout, interval).until_not(presence_of_element_located((by, value)))
        except:
            return False

    def wait_presence_of_elements(self, by, value, timeout=10, interval=0.5) -> List[WebElement]:
        """
            等待多个元素出现
        :param by:查找依据
        :param value:查找元素
        :param timeout:等待最长时间
        :param interval:检测时间间隔
        :return:查找到的所有元素,如果没有找到返回空列表
        """
        try:
            return WebDriverWait(self, timeout, interval).until(presence_of_all_elements_located((by, value)))
        except:
            return []

    def wait_title_contains(self, content, timeout=2, interval=0.5):
        """
            等待判断标题是否包含指定字符
        :param content:是否包含的字符
        :param timeout:等待最长时间
        :param interval:检测时间间隔
        :return:bool类型,是否包含
        """
        try:
            return WebDriverWait(self, timeout, interval).until(title_contains(content))
        except TimeoutException:
            return False

    def wait_url_contains(self, content, timeout=2, interval=0.5):
        """
            等待地址栏是否包含指定字符
        :param content:是否包含的字符
        :param timeout:等待最长时间
        :param interval:检测时间间隔
        :return:bool类型,是否包含
        """
        try:
            return WebDriverWait(self, timeout, interval).until(url_contains(content))
        except TimeoutException:
            return False

    def move_to_element(self, by, value):
        """
            鼠标移动都某个元素
        :param by:查找依据
        :param value:值
        :return:bool类型,是否移动成功
        """
        try:
            action = ActionChains(self)
            action.move_to_element(self.wait_presence_of_element(by, value))
            action.perform()
            return True
        except:
            return False

    def js_click_by_any(self, by, value, timeout=5, interval=0.5):
        """
            通过JavaScript单击元素
            当元素无法点击，报错ElementClickInterceptedException时使用
        :param by:查找依据
        :param value:查找元素
        :param timeout:等待最长时间
        :param interval:检测时间间隔
        """
        element = self.wait_presence_of_element(by, value, timeout, interval)
        self.js_click_by_element(element)

    def js_click_by_element(self, element: WebElement):
        """
            通过JavaScript单击元素
            当元素无法点击，报错ElementClickInterceptedException时使用
        :param element:需要点击的元素
        """
        if element:
            self.execute_script("arguments[0].click();", element)

    @staticmethod
    def download_image(url, save_name):
        """
            下载图片
        :param url:图片的网络地址
        :param save_name:图片的本地路径
        """
        # try:
        #     img_content = requests.get(url).content
        #     # webp类型图片另存为指定格式
        #     img = Image.open(BytesIO(img_content))
        #     img.load()
        #     img.save(save_name)
        # except:
        #     print(url, "|||", img_content)
        with open(save_name, "wb") as file:
            img_content = requests.get(url).content
            file.write(img_content)

    def download_video_for_douyin(self, page_url, save_name):
        """
            下载抖音短视频
        :param save_name:本地文件名
        :param page_url:网络下载地址
        """
        self.get(page_url)
        if self.wait_url_contains("www.douyin.com", 10):
            try:
                video_url = self.__get_video_url_for_douyin()
                with open(save_name, "wb") as file:
                    file.write(requests.get("https:" + video_url).content)
            except Exception as e:
                # 视频不存在 或者 页面中是图片而非视频
                with open(save_name + ".txt", "w") as file:
                    file.write(str(e))

    def switch_window(self, index):
        """
            切换浏览器中的页面
        :param index:需要切换的页面索引
        """
        windows = self.window_handles
        self.switch_to.window(windows[index])

    def is_visible(self, element):
        """
            判断元素是否显示在屏幕中
        :param element:页面中的元素WebElement类型
        """
        return self.execute_script("""
            var elem = arguments[0],                 // 元素对象
                box = elem.getBoundingClientRect(), // 元素位置
                cx = box.left + box.width / 2,       // 元素中心点 x 坐标
                cy = box.top + box.height / 2,       // 元素中心点 y 坐标
                e = document.elementFromPoint(cx, cy);// 获取元素下面的元素

            // 如果元素可见且在当前窗口的可见区域内，返回 true，否则返回 false
            return (e === elem || elem.contains(e)) &&
                   (cx >= 0 && cx <= (window.innerWidth || document.documentElement.clientWidth)) &&
                   (cy >= 0 && cy <= (window.innerHeight || document.documentElement.clientHeight));
        """, element)

    def __get_video_url_for_douyin(self):
        """
            获取视频下载地址
        :return:视频下载地址
        """
        str_encoding = \
            re.findall('<script id="RENDER_DATA" type="application/json">(.*?)</script>', self.page_source)[0]
        # url解码
        # https://tool.ip138.com/urlencode/
        str_decoding = parse.unquote(str_encoding)
        # dict_json = json.loads(str_decoding)
        # video_url = dict_json["41"]["aweme"]["detail"]["video"]["playAddr"][0]["src"]
        return re.findall('"playAddr":\[{"src":"(.*?)"', str_decoding)[0]

    @staticmethod
    def __get_user_data_dir():
        os_name = platform.system()
        if 'Windows' == os_name:
            return r"C:\Users\%s\AppData\Local\Google\Chrome\User Data" % os.getlogin()
        if 'Linux' == os_name:
            return "/home/%s/.config/google-chrome" % os.getlogin()
        if 'Darwin' == os_name:
            return r"/Users/%s/Library/Application\ Support/Google/Chrome" % os.getlogin()
